/* Copyright 2017, 2018, Gabriel Czernikier
 *
 * This file is part of Notehaus.
 * Notehaus is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * Notehaus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with Notehaus.  If not, see <http://www.gnu.org/licenses/>.
 */
#define _BSD_SOURCE
#include <endian.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "class_def.h"
#include "composer.h"
#include "lgwseq.h"
#include "types.h"

struct oclass_def_st {
	struct class_def_st *theclass;  // the target class/interface
	struct list_head unmatched_parent_lst;  // !=EMPTY or ==EMPTY as the discriminator of parent_exhaustion_compar function, element type is struct oclass_def_st
	int unmatched_parent_remain;  // The same meaning than above but with less information. Only the number of such parents. This option is in use preferredly than the above, which is left as fallback should anything unexpected comes.
	struct list_head child_head; // direct subclasses and/or subinterfaces, of type struct oclass_def_ptr_st
}
struct oclass_def_ptr_st {
	struct oclass_def_st *ptr;
	struct list_head oclass_def_ptr_lst;
}
LIST_HEAD(class_def_head);  // struct class_def_st
unsigned long class_def_list_size = 0;
struct oclass_def_st *oclass_def_ary; // supports both sorted views of class_def_st set, first by full class name and later by parent->child relation
int fullname_compar(struct oclass_def_st *a, struct oclass_def_st *b);
int typ_clstyp_compar(struct type_id_st *a, struct oclass_def_st *b);
int parent_exhaustion_compar(struct oclass_def_st *a, struct oclass_def_st *b);

build_oclass_def_ary() {
	// prepares the ground
	oclass_def_ary = malloc(class_def_list_size*sizeof(struct oclass_def_st));
	struct class_def_st *tmp;
	unsigned long i=0;
	list_for_each_entry(tmp, struct class_def_st, class_def_list, class_def_head) {
		oclass_def_ary[i].theclass = tmp;
		oclass_def_ary[i].unmatched_parent_remain=0;
		INIT_LIST_HEAD(&oclass_def_ary[i].child_head);
		i++;
	}
	/* 
	 * struct class_def_st (defined in class_def.h) = {super_class_def_lst (=NULL for "java.lang.Object" and hierarchy-top ifaces)}
	 * UPSTREAM:
	 * super_class_def_lst = [ {class_def, reftype_kind= "CLASS" or "IFACE" } ]
	 * DOWNSTREAM:
	 * step 1: class_def->{superclass_type_idx, super_iface_type_idx_set} and type_id_ptr_client=[X...], of which superclass_type_idx is a member, as they are all of super_iface_type_idx_set.
	 * step 2: all of (*type_id_ptr_client)->str_id are valid after type resolution over unresolver_type.
	 * goes from an unresolved type_id into a type_id by means of type resolution.
	 * step 3: [str_id.idx] are all uniqued by build_ostr_id_and_pack_str_dt. Likewise [type_id.idx] are all uniqued by build_otype_id.
	 */
	qsort(oclass_def_ary, class_def_list_size, sizeof(struct oclass_def_st), (int(*)(void*,void*))fullname_compar);
	/* step 4*: inversion of references class_def->super_type_id and each class_def->super_iface_type_id_set to (super_)class_def->sub_class_def_set via search function 
	 * over oclass_def (firstly sorted by class_def.type_id.idx), such a function yields "aclass_def" and is defined as {search_key_type_id.idx == "aclass_def"->type_idx.idx}, 
	 * and is run with search_keys from super_type_id and each of super_iface_type_id_set. Then, the outcomes are streammed to array of type and supported by struct class_def_onsorting_st
	 * defined next.
	 */
	// TODO validate fullclassname uniqueness over oclass_def_ary, by simple linear walkthrough and struct oclass_def_st.theclass.clstyp.idx comparisson
	for(struct oclass_def_st *oclass=oclass_def_ary; oclass!=oclass_def_ary+class_def_list_size; oclass++) {
		if(oclass->theclass->parenttyp!=NULL) {
			struct oclass_def_st *oparentcls = bsearch(oclass->theclass->parenttyp, oclass_def_ary, class_def_list_size, sizeof(struct oclass_def_st), (int(*)(void*,void*))typ_clstyp_compar);
			struct oclass_def_ptr_st *child = malloc(sizeof(struct oclass_def_ptr_st));
			child->ptr = oclass;
			list_add(&child->oclass_def_ptr_lst, &oparentcls->child_head);
			oclass->unmatched_parent_remain++;
		}
		if(oclass->theclass->parent_iface!=NULL) {
			struct type_member_st *tmp;
			list_foreach_entry(tmp, struct type_member_st, type_member_list,  &oclass->theclass->parent_iface->type_member_head) {
				struct oclass_def_st *oparentcls = bsearch(tmp->type_id, oclass_def_ary, class_def_list_size, sizeof(struct oclass_def_st), (int(*)(void*,void*))typ_clstyp_compar);
				struct oclass_def_ptr_st *child = malloc(sizeof(struct oclass_def_ptr_st));
				child->ptr = oclass;
				list_add(&child->oclass_def_ptr_lst, &oparentcls->child_head);
				oclass->unmatched_parent_remain++;
			}
		}
	}
	/*
	 * step 5: having struct class_def_onsorting_st = {DOWNWARDREF, short lasting UPWARDREF}, DOWNWARDREF={class_def, sub_class_def_lst (classes and ifaces)}, UPWARDREF={class_def, unmatched_super_lst (=NULL for "java.lang.Object" and hierarchy-top ifaces)
	 * class_def_onsorting_st_compar -> { unmatched_super_lst==NULL < unmatched_super_lst!=NULL }
	 * oclass_def_ary = [class_def_onsorting], and oclass_def_ary = [ x : { unmatched_super_lst==NULL } ]  <<EDGE>> [ y : { unmatched_super_lst!=NULL } ]
	 * step 6: from recursively halving (binary splitting) oclass_def_ary[<<last EDGE>> .. #oclass_def_ary], let c be the cases of <<last EDGE>> .. #oclass_def_ary, 
	 * then <<new EDGE>> = c-1 iif oclass_def_ary[c].unmatched_super_lst!=NULL and oclass_def_ary[c-1].unmatched_super_lst==NULL
	 * step 7: foreach C oclass_def_ary[<<last EDGE>> .. <<new EDGE>>] ==> prepare c to be removed from client class_def's unmatched_super_lst, i.e. match c so it gets removed next (TODO how?: best bet-> a flag in struct class_def_st)
	 * foreach c oclass_def_ary[<<new EDGE>> .. #oclass_def_ary] ==> remove unmatched_supper_lst's elements that are just matched
	 * 
	 * (*) oclass_def from stem 4 is not the same as oclass_def_ary from step 5, given that step 4's is sorted by class_def.type_id.idx, not like step 5's which is sorted as 
	 * function class_def_onsorting_st_compar therein defined states.
	 * step 4 is also where class-name non-duplication validation will take place, given that oclass_def defined therein fits into that purpose.
	 * 
	 */
	struct oclass_def_st *edge=oclass_def_ary;
	while(edge!=oclass_def_ary+class_def_list_size) {
		qsort(edge, oclass_def_ary+class_def_list_size-edge, sizeof(struct oclass_def_st), (int (*)(void*,void*))parent_exhaustion_compar); 
		do {
			do {
				struct oclass_def_ptr_st *child;
				list_pop_entry(child, &edge->child_head, struct oclass_def_ptr_st, oclass_def_ptr_lst);
				if(child==NULL)
					break;
				child->ptr->unmatched_parent_remain--;
				free(child);
			} while(1);
			edge++;
		} while(edge!=oclass_def_ary+class_def_list_size && edge->unmatched_parent_remain==0);
	}
}

int fullname_compar(struct oclass_def_st *a, struct oclass_def_st *b) {
	/* sorting by fullname (the class/reftype full name) involves abstraction about the rest of the path
	 * that brings to class names access and comparission. For the explained in steps 1, 2 and 3 of the build_oclass_def_ary function, only the struct type_id_st.idx is used in its stand.
	 */
	return a->theclass->class_type->idx-b->theclass->class_type->idx;
}

int typ_clstyp_compar(struct type_id_st *a, struct oclass_def_st *b) {
	return a->idx-b->theclass->clstyp->idx;
}

int parent_exhaustion_compar(struct oclass_def_st *a, struct oclass_def_st *b) {
	return a->unmatched_parent_remain==0?a->unmatched_parent_remain-b->unmatched_parent_remain:b->unmatched_parent_remain==0?1:0;
}
